﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "Wall.h"
#include "IBody.h"
#include "IElementParentReporter.h"
#include "IGenericElement.h"
#include "IDocument.h"
#include "DbObjectUtils.h"
#include "IElementBasicInformation.h"
#include "CategoryDefine.h"
#include "IRegenerator.h"
#include "IRegeneratorDataIdCreator.h"
#include "IElementParameters.h"
//组件
#include "WallRegenerationComponent.h"
#include "WallViewSpecificShapeComponent.h"
#include "WallUiManipulatorComponent.h"
#include "WallPositionPoints.h"
#include "WallParametersCustomizer.h"
#include "WallTransformationComponent.h"
//图形表示
#include "IElementModelShape.h"
#include "IGraphicsElementShape.h"
//几何
#include "ILine3d.h"
#include "Vector2dUtils.h"
#include "ILine2d.h"
#include "IPolyCurve.h"
#include "IPolygon.h"
#include "GmBodyBuilder.h"
#include "WallManipulateStrategy.h"
#include "ElementUiManipulateStrategyRegister.h"
#include "IElementAttributes.h"
#include "IGeometryRelationshipComponent.h"
#include "WallCutterComponent.h"
#include "WallCutteeComponent.h"
#include "ICutRelationshipBehavior.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace Sample;

IMPLEMENT_REGEN_FUNCTIONS(Sample, Wall, Data)
IMPLEMENT_REGEN_FUNCTIONS(Sample, Wall, WallTagId)


DBOBJECT_DATA_DEFINE(Wall)
{
    SetOwnerElement(nullptr);
#if USE_SERIALIZE
#else
    SetName__(L"test");
#endif
}

UniIdentity Wall::GetImplementationUid()
{
    static UniIdentity s_catogryUid = UniIdentity::Create(GuidUtils::FromString(L"{8328E8FA-38DE-3D55-F5AC-637448B4975E}"), L"Wall");
    return s_catogryUid;
}

bool Wall::SetOwnerElement(IElement* pOwnerElement)
{
    if (pOwnerElement == nullptr)
    {
        return true;
    }
    m_OwnerElement = pOwnerElement;
    return true;
}

const IElement* Wall::GetOwnerElement() const
{
    return GetOwnerElement__();
}

IElement* Wall::GetOwnerElement()
{
    return GetOwnerElement__();
}

gcmp::IDocument* Wall::GetDocument() const
{
    return GetOwnerElement()->GetDocument();
}

ElementId Wall::GetOwnerElementId() const
{
    return GetOwnerElement()->GetElementId();
}

#if USE_SERIALIZE
WallData* Wall::GetData()
{
    return m_DbObject->GetData();
}

void Wall::SetDbObject(gcmp::OwnerPtr<WallObject> opObject)
{
    SetDbObject__(opObject);
}

const WallData* Wall::GetData() const
{
    return m_DbObject->GetData();
}

WallObject* Wall::GetDbObject()
{
    return GetDbObjectFW__().get();
}
#else
std::wstring Wall::GetName() const
{
    return GetName__();
};

void Wall::SetName(const std::wstring& szName)
{
    SetName__(szName);
};

void Wall::SetData(OwnerPtr<WallData> opData)
{
    SetData__(opData);
}

const WallData* Wall::GetData() const
{
    return GetData__().get();
}

WallData* Wall::GetData()
{
    return GetDataFW__().get();
}
#endif

ElementId Wall::GetWallTagId() const
{
    return GetWallTagId__();
}

bool Wall::SetWallTagId(const ElementId& wallTagId)
{
    if (wallTagId == GetWallTagId())
        return true;
    SetWallTagId__(wallTagId);
    return true;
}

void Wall::UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds)
{
    IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"IElement为空", L"GDMP", L"2023-09-30");
    gcmp::IGeometryRelationshipComponent* pGeometryRepCpt = pElement->GetGeometryRelationshipComponent();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGeometryRepCpt, L"pGeometryRepCpt为空", L"GDMP", L"2023-09-30");
    ICutRelationshipBehavior* pCutRelationshipBehavior = pGeometryRepCpt->GetCutRelationship();
    if (pCutRelationshipBehavior)
    {
        FOR_EACH(cutId, deletedElementIds)
        {
            pCutRelationshipBehavior->DeleteElementCutId(cutId);
        }
    }
}

void Wall::ReportParents(IElementParentReporter& reporter) const
{
    const IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"IElement为空", L"GDMP", L"2023-09-30");
    reporter.ReportStrong(pElement->GetElementId());
    reporter.ReportStrong(pElement->GetBasicInformation()->GetTypeId());
    const IGraphicsElementShape* pGraphicsElementShape = pElement->GetElementModelShape()->GetGraphicsElementShape();
    if (pGraphicsElementShape)
    {
        std::vector<ElementId> ids;
        pGraphicsElementShape->GetReferencedElementIds(ids);
        reporter.ReportWeak(ids);
    }

    const gcmp::IGeometryRelationshipComponent* pGeometryRepCpt = pElement->GetGeometryRelationshipComponent();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGeometryRepCpt, L"pGeometryRepCpt为空", L"GDMP", L"2023-09-30");

    const ICutRelationshipBehavior* pCutRelationshipBehavior = pGeometryRepCpt->GetCutRelationship();
    if (pCutRelationshipBehavior)
    {
        std::vector<ElementId> cutIds = pCutRelationshipBehavior->GetElementCutIds();
        FOR_EACH(cutId, cutIds)
        {
            reporter.ReportWeak(cutId);
        }
    }
    reporter.ReportWeak(GetWallTagId());
}

bool Wall::FinishEditing()
{
    return true;
}

Wall* Wall::Create(IDocument* pDoc, ElementCreationOptions eco /*= ElementCreationOptions::Normal*/)
{
    OwnerPtr<Wall> opWall = NEW_AS_OWNER_PTR(Wall);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opWall, L"创建Wall失败", L"GDMP", L"2023-09-30");
    IGenericElement* pGenericElement = IGenericElement::Create(pDoc, TransferOwnership(opWall),
        Wall::GetClassId().GetGuid(), eco);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGenericElement, L"创建IGenericElement失败", L"GDMP", L"2023-09-30");
    //设置类别
    pGenericElement->GetBasicInformation()->SetCategoryUid(Sample::WALL_CATEGORY);
    //设置自定义参数
    pGenericElement->GetElementParameters()->SetElementParametersCustomizer(NEW_AS_OWNER_PTR(WallParametersCustomizer));
    //设置关联更新组件
    pGenericElement->SetElementRegenerationComponent(NEW_AS_OWNER_PTR(WallRegenerationComponent));
    //设置控制点信息
    pGenericElement->SetPositionPoints(NEW_AS_OWNER_PTR(WallPositionPoints));
    //夹点绑定
    pGenericElement->GetBasicInformation()->SetImplementationUid(GetImplementationUid());
    //设置视图组件
    pGenericElement->SetViewSpecificShapeComponent(
        TransferOwnership(NEW_AS_OWNER_PTR(WallViewSpecificShapeComponent)));

    //设置矩阵变换组件
    pGenericElement->SetElementTransformationComponent(NEW_AS_OWNER_PTR(WallTransformationComponent));
    // 设置几何关系组件
    IGeometryRelationshipComponent* pGeometryRepCmpt = pGenericElement->GetGeometryRelationshipComponent();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGeometryRepCmpt, L"pGeometryRepCmpt无效", L"GDMP", L"2023-09-30");
    pGeometryRepCmpt->SetCutterGraphicsElementShapeComponent(TransferOwnership(NEW_AS_OWNER_PTR(WallCutterComponent, pGenericElement)));
    pGeometryRepCmpt->SetCutteeGraphicsElementShapeComponent(TransferOwnership(NEW_AS_OWNER_PTR(WallCutteeComponent, pGenericElement)));

    Wall* pWall = quick_cast<Wall>(pGenericElement->GetExternalObject());
    return pWall;
}

Wall* Wall::Create(IDocument* pDoc, const Vector3d& startPt, const Vector3d& endPt, ElementCreationOptions eco /*= ElementCreationOptions::Normal*/)
{
    Wall* pWall = Create(pDoc, eco);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pWall, L"创建Wall失败", L"GDMP", L"2023-09-30");

    OwnerPtr<WallData> opData = NEW_AS_OWNER_PTR(WallData);
    opData->SetStart(startPt);
    opData->SetEnd(endPt);

#if USE_SERIALIZE
    OwnerPtr<WallObject> opObject = NEW_AS_OWNER_PTR(WallObject);
    opObject->SetData(TransferOwnership(data));
    pWall->SetDbObject(TransferOwnership(opObject));
#else
    pWall->SetData(TransferOwnership(opData));
#endif
    pWall->MarkDataRdId();

    IGenericElement *pGenericElement = quick_cast<IGenericElement>(pWall->GetOwnerElement());
    IElementAttributes* attr = pGenericElement->GetAttributes();
    attr->AddAttributeWithValueAsString(L"Company", L"Glodon");
    return pWall;
}

OwnerPtr<IBody> Wall::MakeBody()
{
    Vector2d ptStart = GetData()->GetStartPoint().Vec2d();
    Vector2d ptEnd = GetData()->GetEndPoint().Vec2d();
    Vector2d perpDir = Vector2dUtils::Rotate(ptEnd - ptStart, Constants::MATH_PI_2);
    perpDir.Normalize();

    Vector2d pt1 = ptStart + perpDir * GetData()->GetWidth() / 2;
    Vector2d pt2 = ptStart - perpDir * GetData()->GetWidth() / 2;
    Vector2d pt3 = ptEnd - perpDir * GetData()->GetWidth() / 2;
    Vector2d pt4 = ptEnd + perpDir * GetData()->GetWidth() / 2;

    OwnerPtr<IPolyCurve> opCurve = IPolyCurve::Create();
    opCurve->AddBackCurve(ILine2d::Create(pt1, pt2));
    opCurve->AddBackCurve(ILine2d::Create(pt2, pt3));
    opCurve->AddBackCurve(ILine2d::Create(pt3, pt4));
    opCurve->AddBackCurve(ILine2d::Create(pt4, pt1));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opCurve->IsClosed(), L"多边形不合法", L"GDMP", L"2023-09-30");
    if (!opCurve->IsAnticlockwise())
    {
        opCurve->Reverse();
    }
    OwnerPtr<IPolygon> opPolygon = IPolygon::Create();
    opPolygon->AddPolyCurveByClone(opCurve.get());
    double dLevel = GetData()->GetStartPoint().GetZ();
    Coordinate3d coord3d;
    return GmBodyBuilder::CreateExtrudeBody(coord3d, opPolygon.get(), Vector3d::UnitZ, dLevel, dLevel + GetData()->GetHeight());
}

